//
//  JKPlayerKit.m
//  JKPlayerKit
//
//  Created by 王冲 on 2019/2/9.
//  Copyright © 2019年 JK科技有限公司. All rights reserved.
//

#import "JKPlayerKit.h"
#import "JKPlayerResourceLoaderDelegate.h"
// #import "JKPlayerTestResourceLoaderDelegate.h"
#import "NSURL+Extension.h"
@interface JKPlayerKit ()
{
    // 用户是否进行了手动暂停
    BOOL isUserPause;
}

/**
 代理属性
 */
@property(nonatomic,strong) JKPlayerResourceLoaderDelegate *resourceLoaderDelegate;

/**
 播放器
 */
@property(nonatomic,strong) AVPlayer *player;


@end

@implementation JKPlayerKit

static id instance;

/**
 单利：
 1.无论通过怎样的方式，创建出来，只有一个实例（alloc,new,muableCopy）
 2.通过某种方式，可以获取同一个对象，但是也开通过其他方式，创建出来新的对象
 */
+(instancetype)allocWithZone:(struct _NSZone *)zone{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    
    return instance;
}

-(id)copy{
    
    return instance;
}

-(id)mutableCopy{
    
    return instance;
}

+(instancetype)shareDownloaderManger{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        instance = [[self alloc]init];
    });
    
    return instance;
}

/**  播放视频 */
-(void)playerWithUrl:(NSURL *)url isCache:(BOOL)isCache{
    
    // 先判断要播放的 url 与之前播放的 url 是否相等
    NSURL *currentURL = [(AVURLAsset *)self.player.currentItem.asset URL];
    
    if ([url isEqual: currentURL] || [[url stremingURL] isEqual:currentURL]) {
        
        JKLog(@"当前播放任务已经存在");
        [self resume];
        return;
    }
    
    // 在执行下面的2之前先进行一下判断，如果存在AVPlayerItem，先移除
    if (self.player.currentItem) {
        [self removeObserver];
    }
    
    // 如果需要缓存，就更改协议，如果不缓存那么系统对资源进行加载
    
    // 记录url
    _url = url;
    
    if (isCache) {
        
        url = [url stremingURL];
    }
    
    // 1.创建一个播放器对象
    /**
     
      AVPlayer *player = [AVPlayer playerWithURL:url];
      [player play];
     
      使用下面的方法播放远程的视频，如果加载资源比较慢，有可能会造成调用了play方法，视频会播放不了,
       在这个方法已经帮我们封装了三个步骤
       步骤一、资源的请求
       步骤二、资源的组织
       步骤三、给播放器，资源的播放
     
     */
    // 1、资源的请求
    AVURLAsset *asset = [AVURLAsset assetWithURL:url];
    
    // 关于网络音频的请求，是通过这个对象，调用代理的相关方法，进行加载的，拦截加载的请求，只需要重新修改它的的代理方法就可以
    self.resourceLoaderDelegate = [JKPlayerResourceLoaderDelegate new];
    [asset.resourceLoader setDelegate:self.resourceLoaderDelegate queue:dispatch_get_main_queue()];
    
    // 2、资源的组织(当资源的组织者，告诉我们资源准备好了之后，我们再去播放)
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
    /**
       2.1监听状态去播放视频
       @property (nonatomic, readonly) AVPlayerItemStatus status;
     */
    [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    
    /**
       2.2、
       playbackLikelyToKeepUp
     */
    [playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
    
    /**
       2.3、监听播放结束
     */
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(playEnd) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    
    /**
       2.4、播放被打断
     */
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(playInterrupt) name:AVPlayerItemPlaybackStalledNotification object:nil];

    // 3、给播放器，资源的播放
    self.player = [AVPlayer playerWithPlayerItem:playerItem];
}

#pragma mark KVO 监听
// 有关这个类里面的监听
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    // 刚开始的时候走这里，在播放的过成功中走下面playbackLikelyToKeepUp的监听方法
    if ([keyPath isEqualToString:@"status"]) {
        
        AVPlayerItemStatus status = [change[NSKeyValueChangeNewKey] integerValue];
        if (status == AVPlayerItemStatusReadyToPlay) {
            
            // 资源准备好了，可以进行播放
            JKLog(@"资源准备好了，可以进行播放");
            [self resume];
        }else{
            
            JKLog(@"状态未知");
            self.state = JKPlayerStateFailed;
        }
    }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]){
        
        BOOL pToKeepUp = [change[NSKeyValueChangeNewKey] boolValue];
        if (pToKeepUp) {
            
            // 这里是指有可缓冲好的资源去播放
            JKLog(@"资源已经准备好了,可以进行播放");
            // 用户手动暂停的优先级最高
            if (!isUserPause) {
                
                // 用户没有手动暂停才去播放的
                [self resume];
            }
        }else{
            
            JKLog(@"资源准备中，也就是在正在加载");
            self.state = JKPlayerStateLoading;
        }
    }
}

#pragma mark 播放结束
-(void)playEnd{
    
    JKLog(@"播放结束");
    self.state = JKPlayerStateStopped;
}

#pragma mark 播放被打断
-(void)playInterrupt{
    
    JKLog(@"播放被打断");
    /**
     原因：
     1.来电话
     2.资源加载跟不上
     */
    self.state = JKPlayerStateStopped;
    
}

#pragma mark 移除监听
-(void)removeObserver{
    
    // status
    [self.player.currentItem removeObserver:self forKeyPath:@"status"];
    // playbackLikelyToKeepUp
    [self.player.currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
}

/**
 暂停播放
 */
-(void)pause{
    
    [self.player pause];
    
    // 手动暂停
    isUserPause = YES;
    
    // 先判断播放器存在
    if (self.player) {
        self.state = JKPlayerStatePause;
    }
}

/**
 继续播放
 */
-(void)resume{
    
    [self.player play];
    
    // 没有手动暂停
    isUserPause = NO;
    
    // 1.当前播放器存在，2.组织者里面的数据已经准备的可以播放了
    if (self.player && self.player.currentItem.playbackLikelyToKeepUp) {
        // 改状态为正在播放
        self.state = JKPlayerStatePlaying;
    }
}

/**
 停止播放
 */
-(void)stop{
    
    [self.player pause];
    
    // 先判断播放器存在
    if (self.player) {
        self.state = JKPlayerStateStopped;
    }
    
    self.player = nil;
}

/**
 快进
 
 @param timeDiffer 快进的时间
 */
-(void)seekWithTimeDiffer:(NSTimeInterval)timeDiffer{
    
    // 1.当前音频资源的总时长
    // CMTime totalTime = self.player.currentItem.duration;
    // 资源总秒数: 影片时间 -> 秒
    // NSTimeInterval totalSecond = CMTimeGetSeconds(totalTime);
    NSTimeInterval totalSecond = [self totalTime];
    // 2.当前播放的总时长
    //CMTime playTime =  self.player.currentItem.currentTime;
    // NSTimeInterval playTimeSecond = CMTimeGetSeconds(playTime);
    NSTimeInterval playTimeSecond = [self currentTime];
    // 3.更新快进后的时间（ -15 或者 +15 ）
    playTimeSecond += timeDiffer;
    
    // 调用进度播放
    [self seekWithTimeProgress:playTimeSecond/totalSecond];
}

/**
 指定播放进度
 
 @param progress 进度值
 */
-(void)seekWithTimeProgress:(float)progress{
    
    if (progress < 0 || progress > 1) {
        return;
    }
    
    /**
     指定时间节点去播放
     时间：CMTime（影片时间）
     影片时间 -> 秒
     秒 -> 影片时间
     */
    // 1.当前音频资源的总时长(用下面的方法([self totalTime])替换掉)
    // CMTime totalTime = self.player.currentItem.duration;
    /**
       2.当前音频，已经播放的时长
       self.player.currentItem.currentTime
     */
    // 资源总秒数: 影片时间 -> 秒
    NSTimeInterval totalSecond = [self totalTime];
    NSTimeInterval playTimeSecond = totalSecond * progress;
    
    /**
      秒 -> 影片时间
      第一个参数：秒数
      第二个参数：每秒多少帧
     */
    CMTime currentTime = CMTimeMake(playTimeSecond, 1);
    [self.player seekToTime:currentTime completionHandler:^(BOOL finished) {
        
        if (finished) {
            
            JKLog(@"确定加载的秒数：%f",playTimeSecond);
            
            JKLog(@"确定加载这个时间点的音频资源");
        }else{
            
            // 有可能在加载出来之前拖动多次
            JKLog(@"取消加载这个时间点的音频资源");
        }
    }];
}

/**
 播放的倍速
 
 @param rate 倍速值：1.0、2.0、3.0
 */
-(void)setRate:(float)rate{
    
    [self.player setRate:rate];
}

-(float)rate{
    
   return self.player.rate;
}

/**
 是否静音
 
 @param muted YES：静音 NO：不静音
 */
-(void)setMuted:(BOOL)muted{
    
    self.player.muted = muted;
}

-(BOOL)muted{
    
   return self.player.muted;
}

/**
 音量调整
 
 @param volume 音量的大小
 */
-(void)setVolume:(float)volume{
    
    if (volume < 0 || volume > 1) {
        return;
    }
    
    if (volume > 0) {
        
        // 音量大于0,取消静音
        [self setMuted:NO];
    }
    
    self.player.volume = volume;
    
}

-(float)volume{
    
    return self.player.volume;
}

#pragma mark 播放器-事件&数据提供

// 当前音频资源的总时长
-(NSTimeInterval)totalTime{
    
    // 1.当前音频资源的总时长
    CMTime totalTime = self.player.currentItem.duration;
    /**
     2.当前音频，已经播放的时长
     self.player.currentItem.currentTime
     */
    // 资源总秒数: 影片时间 -> 秒
    NSTimeInterval totalSecond = CMTimeGetSeconds(totalTime);
    // 如果发现 totalSecond = NaN,返回一个0
    if (isnan(totalSecond)) {
        return 0;
    }
    
    return totalSecond;
}

-(NSString *)totalTimeFormat{
    
    return [NSString stringWithFormat:@"%02d:%02d",(int)self.totalTime/60,(int)self.totalTime % 60];
}

// 当前播放的总时长
-(NSTimeInterval)currentTime{
    
    // 2.当前播放的总时长
    CMTime playTime =  self.player.currentItem.currentTime;
    NSTimeInterval playTimeSecond = CMTimeGetSeconds(playTime);
    
    // 如果发现 playTimeSecond = NaN,返回一个0
    if (isnan(playTimeSecond)) {
        return 0;
    }
    
    return playTimeSecond;
}

-(NSString *)currentTimeFormat{
    
    return [NSString stringWithFormat:@"%02d:%02d",(int)self.currentTime/60,(int)self.currentTime % 60];
}

// 当前播放的进度
-(float)progress{
    
    // 容错处理
    if (self.totalTime == 0) {
        return 0;
    }
    
    return self.currentTime/self.totalTime;
}

// 缓存的时长
-(float)loadDataProgress{
    
    // 容错处理
    if (self.totalTime == 0) {
        return 0;
    }
    
    // 获取加载的时间区间
    CMTimeRange timeRange = [[self.player.currentItem loadedTimeRanges].lastObject CMTimeRangeValue];
    // 开始时间与缓存时间和
    CMTime loadTime = CMTimeAdd(timeRange.start, timeRange.duration);
    // 转化成秒
    NSTimeInterval loadTimeSecond = CMTimeGetSeconds(loadTime);
    // 缓存的时间除以总时间得到缓存的进度
    return loadTimeSecond/self.totalTime;
}

/**
  播放的状态
 */
-(void)setState:(JKPlayerState)state{
    
    _state = state;
    
    // 如果需要告诉外界相关的事件,可以选择 block、代理、通知，这些都是 推模式
}

@end
